﻿<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>33_LightingSphere</title>
    <link href="css/common.css" rel="stylesheet" />
    <script src="js/gl-matrix.js"></script>
</head>
<body>
    <canvas id="mycanvas"></canvas>
    <script id="shader-vs" type="x-shader/x-vertex">
        attribute vec4 aPosition;
        attribute vec4 aColor;
        attribute vec4 aNormal;

        uniform mat4 uModelMatrix;
        uniform mat4 uViewMatrix;
        uniform mat4 uProjMatrix;

        uniform vec3 uDiffuseLightColor;
        uniform vec3 uDiffuseLightDirection;
        uniform vec3 uAmbientLight;
        uniform vec3 uPointLightColor;
        uniform vec3 uPointLightPosition;

        varying vec4 vColor;

        void main() {
        gl_Position = uProjMatrix *  uViewMatrix * uModelMatrix * aPosition;
        vec4 translateNormal = uModelMatrix * aNormal;
        vec3 normal = normalize(translateNormal.xyz);

        float nDotDL = max(dot(uDiffuseLightDirection, normal), 0.0);
        vec3 diffuseDL = uDiffuseLightColor * aColor.rgb * nDotDL;

        vec3 ambient = uAmbientLight * aColor.rgb;
        
        vec4 vertexPosition = uModelMatrix * aPosition;
        vec3 lightDirection = normalize(uPointLightPosition - vec3(vertexPosition));
        float nDotPL = max(dot(lightDirection, normal), 0.0);
        vec3 diffusePL = uPointLightColor * aColor.rgb * nDotPL;
        
        vColor = vec4(diffuseDL + ambient + diffusePL, aColor.a);
        }
    </script>
    <script id="shader-fs" type="x-shader/x-fragment">
        precision mediump float;

        varying vec4 vColor;

        void main() {
        gl_FragColor = vColor;
        }
    </script>
    <script>
        var canvas = document.getElementById('mycanvas');
        canvas.width = canvas.clientWidth;
        canvas.height = canvas.clientHeight;

        var gl = canvas.getContext('webgl');

        var vertexShader = gl.createShader(gl.VERTEX_SHADER);
        gl.shaderSource(vertexShader, document.getElementById('shader-vs').innerHTML);
        gl.compileShader(vertexShader);

        var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
        gl.shaderSource(fragmentShader, document.getElementById('shader-fs').innerHTML);
        gl.compileShader(fragmentShader);

        var shaderProgram = gl.createProgram();
        gl.attachShader(shaderProgram, vertexShader);
        gl.attachShader(shaderProgram, fragmentShader);

        gl.linkProgram(shaderProgram);
        var linked = gl.getProgramParameter(shaderProgram, gl.LINK_STATUS);
        if (!linked) {
            var error = gl.getProgramInfoLog(shaderProgram);
            console.log(error);
        }
        gl.useProgram(shaderProgram);

        var aPosition = gl.getAttribLocation(shaderProgram, 'aPosition');
        var aColor = gl.getAttribLocation(shaderProgram, 'aColor');
        var aNormal = gl.getAttribLocation(shaderProgram, 'aNormal');
        var uModelMatrix = gl.getUniformLocation(shaderProgram, 'uModelMatrix');
        var uViewMatrix = gl.getUniformLocation(shaderProgram, 'uViewMatrix');
        var uProjMatrix = gl.getUniformLocation(shaderProgram, 'uProjMatrix');
        var uDiffuseLightColor = gl.getUniformLocation(shaderProgram, 'uDiffuseLightColor');
        var uDiffuseLightDirection = gl.getUniformLocation(shaderProgram, 'uDiffuseLightDirection');
        var uAmbientLight = gl.getUniformLocation(shaderProgram, 'uAmbientLight');
        var uPointLightColor = gl.getUniformLocation(shaderProgram, 'uPointLightColor');
        var uPointLightPosition = gl.getUniformLocation(shaderProgram, 'uPointLightPosition');

        // 创建球的顶点坐标、颜色和索引数组
        var positions = [];
        var colors = [];
        var indices = [];

        var SPHERE_DIV = 64;

        // Generate coordinates
        for (var j = 0; j <= SPHERE_DIV; j++) {
            var aj = j * Math.PI / SPHERE_DIV;
            var sj = Math.sin(aj);
            var cj = Math.cos(aj);
            for (var i = 0; i <= SPHERE_DIV; i++) {
                var ai = i * 2 * Math.PI / SPHERE_DIV;
                var si = Math.sin(ai);
                var ci = Math.cos(ai);

                positions.push(si * sj);  // X
                positions.push(cj);       // Y
                positions.push(ci * sj);  // Z

                colors.push(1.0);
                colors.push(0.0);
                colors.push(0.0);
            }
        }

        // Generate indices
        for (var j = 0; j < SPHERE_DIV; j++) {
            for (i = 0; i < SPHERE_DIV; i++) {
                var p1 = j * (SPHERE_DIV + 1) + i;
                var p2 = p1 + (SPHERE_DIV + 1);

                indices.push(p1);
                indices.push(p2);
                indices.push(p1 + 1);

                indices.push(p1 + 1);
                indices.push(p2);
                indices.push(p2 + 1);
            }
        }

        // 顶点位置矩阵
        var positionBuffer = gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);

        gl.vertexAttribPointer(aPosition, 3, gl.FLOAT, false, 0, 0);
        gl.enableVertexAttribArray(aPosition);

        // 顶点颜色矩阵
        var colorBuffer = gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.STATIC_DRAW);

        gl.vertexAttribPointer(aColor, 3, gl.FLOAT, false, 0, 0);
        gl.enableVertexAttribArray(aColor);

        // 法线矩阵
        var normalBuffer = gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER, normalBuffer);
        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);

        gl.vertexAttribPointer(aNormal, 3, gl.FLOAT, false, 0, 0);
        gl.enableVertexAttribArray(aNormal);

        // 模型矩阵
        var model = mat4.create();
        mat4.rotateX(model, mat4.create(), 0);
        gl.uniformMatrix4fv(uModelMatrix, false, model);

        // 视图矩阵
        var view = mat4.create();
        mat4.lookAt(view, vec3.fromValues(1, 1, 1), vec3.fromValues(0, 0, 0, 0), vec3.fromValues(1, 0, 0));
        gl.uniformMatrix4fv(uViewMatrix, false, view);

        // 投影矩阵
        var proj = mat4.create();
        mat4.perspective(proj, Math.PI * 90 / 180, canvas.width / canvas.height, 0.1, 10000);
        gl.uniformMatrix4fv(uProjMatrix, false, proj);

        // 平行光光照颜色
        gl.uniform3f(uDiffuseLightColor, 0.0, 0.0, 0.0);

        // 平行光光照方向
        var direction = vec3.fromValues(6.0, 3.2, 4.7);
        vec3.normalize(direction, direction);
        gl.uniform3fv(uDiffuseLightDirection, direction);

        // 环境光
        gl.uniform3f(uAmbientLight, 0.1, 0.1, 0.1);

        // 点光源
        gl.uniform3f(uPointLightColor, 0.8, 0.8, 0.8);
        gl.uniform3f(uPointLightPosition, 1.3, 1.2, 1.1);

        // 索引矩阵
        var indexBuffer = gl.createBuffer();
        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
        gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW);

        // 渲染
        gl.clearColor(0.0, 0.0, 0.0, 1.0);
        gl.enable(gl.DEPTH_TEST);
        gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
        gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_SHORT, 0);

        // 左右方向键事件
        var xAxisAngle = 0;
        var yAxisAngle = 0;
        document.onkeydown = function (event) {
            if (event.keyCode == 37) { // 左方向键
                xAxisAngle += 5
            } else if (event.keyCode == 39) { // 右方向键
                xAxisAngle -= 5;
            } else if (event.keyCode == 38) { // 上方向键
                yAxisAngle += 5;
            } else if (event.keyCode == 40) { // 下方向键
                yAxisAngle -= 5;
            }

            mat4.rotateX(model, mat4.create(), Math.PI * xAxisAngle / 180);
            mat4.rotateY(model, model, Math.PI * yAxisAngle / 180);
            gl.uniformMatrix4fv(uModelMatrix, false, model);

            gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
            gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_SHORT, 0);
        }
    </script>
</body>
</html>
